package com.zimin.redis.aop.aspect;

import com.zimin.common.exception.GraceException;
import com.zimin.common.utils.AopUtils;
import com.zimin.common.utils.ObjectUtils;
import com.zimin.redis.utils.RedisOperator;
import com.zimin.shared.annotation.Idempotence;
import com.zimin.shared.enums.ResponseStatusEnum;
import com.zimin.shared.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.stream.IntStream;

/**
 * @author zimin
 * @since 2021/11/4 5:08 下午
 */
@Slf4j
@Aspect
@Component
public class IdempotenceAspect implements Ordered {

    @Autowired
    private RedisOperator redisOperator;

    @Pointcut("@annotation(com.zimin.shared.annotation.Idempotence)")
    public void idempotence() {
    }


    /**
     * <p>幂等性校验的前置通知</p>
     *
     * @param joinPoint   切点
     * @param idempotence 幂等性注解
     * @return java.lang.Object
     * @author zimin
     * @since 2021-11-04 17:37:13
     */
    @Around("@annotation(idempotence)")
    public Object idempotence(ProceedingJoinPoint joinPoint, Idempotence idempotence) throws Throwable {
        var method = AopUtils.getMethod(joinPoint);
        GraceException.isNull(method, ResponseStatusEnum.IDEMPOTENCE_ERROR);
        var parameters = method.getParameters();
        GraceException.isNull(parameters, ResponseStatusEnum.IDEMPOTENCE_ERROR);
        var paramName = idempotence.paramName();
        var u = new LocalVariableTableParameterNameDiscoverer();
        var parameterNames = u.getParameterNames(method);
        var index = IntStream.range(0, parameters.length)
                .filter(value -> paramName.equals(parameterNames[value]))
                .boxed().findFirst().get();
        GraceException.isNull(index, ResponseStatusEnum.IDEMPOTENCE_ERROR);
        var args = joinPoint.getArgs();
        var idempotenceValue = (String) args[index];
        var flag = redisOperator.checkIdempotence(idempotenceValue);
        GraceException.isFalse(flag, ResponseStatusEnum.IDEMPOTENCE_ERROR);
        return joinPoint.proceed();
    }


    @Override
    public int getOrder() {
        return 0;
    }
}
